class_name EcsWorld, "res://ecs/icons/world.svg"

extends Node
signal _ready_contine
signal world_ready

var query_manager := QueryManager.new(self)
var systems: Array = []
var _tps_map: Dictionary
var _registered = false
var initialized = false


func _init():
	print("EcsWorld._init")


func _ready():
	query_manager.name = "QueryManager"
	add_child(query_manager)
	#break here until world_ready signal emitted, so you can do stuff with the world
	yield(self, "_ready_contine")
	systems = get_systems()
	_registered = true

	for system in systems:
		system.requirements.sort()
		query_manager.register_requirements(system)

		var tps: float = system.get("tps") if system.get("tps") else 0.0
		var seconds_per_tick := 1.0 / tps if tps else 0.0
		_tps_map[system] = Vector2(seconds_per_tick, seconds_per_tick)

	# register entity components
	query_manager.ready()

	for system in systems:
		if system.has_method("_system_ready"):
			system.call("_system_ready")

	create_entities()
	initialized = true
	emit_signal("world_ready")


#contine init ecs must be called after all ready
func continue_init():
	emit_signal("_ready_contine")


# create entity after system ready
func create_entities():
	var entities = query_manager.entities
	for entity in get_tree().get_nodes_in_group("Entity"):
		entities.insert(entity.id, entity)
		entities.create(entity.id)


func _process(delta: float) -> void:
	if Engine.editor_hint or not initialized:
		return
	var pause = query_manager.fetch_single("GlobalPause").data
	var frame = query_manager.fetch_single("FrameTime").data
	if pause.value == 1:#world paused
		return
	frame.count += 1
	for system in systems:
		if not system.has_method("_system_process"):
			continue
		var tps_vec: Vector2 = _tps_map[system]
		if tps_vec.x > 0:
			tps_vec.y += delta
			if tps_vec.y < tps_vec.x:
				_tps_map[system] = tps_vec
				continue
			system.call("_system_process", tps_vec.y)
			tps_vec.y -= tps_vec.x
			_tps_map[system] = tps_vec
		else:
			system.call("_system_process", delta)


func _physics_process(delta: float) -> void:
	if Engine.editor_hint or not initialized:
		return
	for system in systems:
		if not system.has_method("_system_physics_process"):
			continue
		system.call("_system_physics_process", delta)


func get_systems() -> Array:
	var results := []
	if _registered:
		return systems
	for child in get_children():
		if ECSTool.is_system(child):
			if Engine.editor_hint:
				results.append(child)
			elif validate_system(child):
				results.append(child)
		elif ECSTool.is_multi(child):
			query_manager.register_multi(child)
		elif ECSTool.is_single(child):
			query_manager.register_single(child)
	return results


func validate_system(system: Node) -> bool:
	if not system.has_method("_system_init"):
		push_error(
			"[System:%s] does not have a _system_init() method. Skipping." % system.system_name
		)
		return false
	if not system.call("_system_init", self):
		push_error(
			"[System:%s]  _system_init() method returned false. Skipping." % system.system_name
		)
		return false
	if not system.requirements:
		push_error(
			"[System:%s] attempted to register with no requirements. Skipping." % system.system_name
		)
		return false
	return true

#--------------------------------------------------------------------------------
